package org.luxor.commons.mvc.handler;

import com.google.common.collect.Maps;
import org.luxor.commons.core.exception.BaseException;
import org.luxor.commons.core.utils.Jackson2Utils;
import org.luxor.commons.core.web.RestStatus;
import org.luxor.commons.core.web.controller.BaseRestController;
import org.luxor.commons.core.web.data.R;
import org.luxor.commons.log.filter.RestLog;
import org.luxor.commons.mvc.adapter.BaseExceptionAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.annotation.Resource;
import java.util.Map;

/**
 * 统一响应消息结构
 *
 * @author Mr.yan @date 2019/9/19
 */
@Configuration
@ControllerAdvice(assignableTypes = {BaseRestController.class})
public class RestResponseBody implements ResponseBodyAdvice<Object> {
    private static final Logger logger = LoggerFactory.getLogger(RestResponseBody.class);

    @Resource
    private BaseExceptionAdapter adapter;

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object object, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        //若返回内容为空的时候，将空Map作为返回对象
        if (object == null) {
            object = Maps.newHashMap();
        }
        if (object instanceof R) {
            return object;
        }

        Map<String, Object> result;
        if (object instanceof BaseException) {
            BaseException exec = ((BaseException) object);
            adapter.handle(exec);
            result = exec.asMap(true);
        } else if (object instanceof RestStatus) {
            result = ((RestStatus) object).asMap();
        } else {
            result = RestStatus.SUCCESS.asMap();
            result.put("data", object);
        }

        if (RestLog.isDebugEnabled()) {
            try {
                RestLog.logResponse(response, result);
            } catch (Exception e) {
                logger.error("LOG_RESPONSE_ERROR", e);
            }
        }

        if (object instanceof String) {
            return Jackson2Utils.toJson(result);
        }
        return result;
    }

    @Bean
    @ConditionalOnMissingBean(name = "baseExceptionAdapter")
    BaseExceptionAdapter baseExceptionAdapter() {
        return exception -> {
        };
    }
}